#!/usr/local/bin/python3
# *_* coding: UTF-8 *_*
# @IDE: PyCharm
# @Version: Python3.97
# @Author: Kendrick.Kc
# @Email: 509556985@qq.com
# @File: filter_all_symbol.py
# @Inst: 过滤所有交易对
# @Time: 2022/8/17 19:07
# -----


import time
import numpy
import pandas
from datetime import datetime


def set_pandas(data: list, columns: list = None):
    data = pandas.DataFrame(data, columns=columns)
    return data


class FilterSymbol:
    """
    获取交易所全部交易对并根据值过滤出高波动率币种
    """

    def __init__(self, binance):
        self.binance = binance

    def get_rv(self, one: int = 0, limit: int = 7, interval: str = '1d', max_limit: int = 1500, fil_symbol=None):
        """
        返回高波动率列表
        :param one: 0:返回所有, 2:只返回第2条 (3/4/5/6/7/8.....)
        :param limit: KBar数量
        :param interval: KBar的时间间隔 (1d/3d......)
        :param max_limit: 交易所最大limit/KBar数量
        :param fil_symbol: 要过滤的交易对
        :return: {'交易对名称': 'BTCUSDT', '实际波动率': 0.0376, '高深度': 1}
        """
        fil_symbol = [] if fil_symbol is None else fil_symbol
        res = self.get_all_symbol(limit=limit, interval=interval, max_limit=max_limit, fil_symbol=fil_symbol)
        return res[:one] if one > 0 else res

    def get_all_symbol(self, limit: int = None, interval: str = None, max_limit: int = None, fil_symbol: list = None):
        """
        获取所有交易对
        :param limit: KBar数量
        :param interval: KBar的时间间隔 (1d/3d......)
        :param max_limit: 交易所最大limit/KBar数量
        :param fil_symbol: 要过滤的交易对
        :return: 交易对列表
        """

        def _filter(_symbol: list):
            """ 过滤币种 """
            for item in _symbol:
                if item in fil_symbol:
                    _symbol.remove(item)
            return self.filter_day(_symbol)

        self.binance.logger_info(f"正在获取所有交易对......")
        data = self.binance.rest('while_info', 'ticker_price')
        symbol_data = _filter([item['symbol'] for item in data if item['symbol'][-4:] == 'USDT'])
        self.binance.logger_info(f"获取所有交易对完成.")
        return self.realized_volatility(symbol=symbol_data, limit=limit, interval=interval, max_limit=max_limit)

    def realized_volatility(self, symbol: list, limit: int = None, interval: str = None, max_limit: int = None):
        """
        计算每个交易对的实际波动率
        :param symbol: 交易对名称列表
        :param limit: KBar数量
        :param interval: KBar的时间间隔 (1d/3d......)
        :param max_limit: 交易所最大limit/KBar数量
        :return: 嵌套字典的交易对列表 {'交易对名称': 'BTCUSDT', '实际波动率': 0.03753695403302971}
        """
        self.binance.logger_info(f"正在获取所有交易对日KBar并计算实际波动率 ### 参数: limit={limit}, interval={interval})......")
        self.binance.logger_info(f"=== 正在验证limit参数......")
        if 3 > limit or limit >= max_limit:
            self.binance.logger_warning(f'参数错误: limit({limit}) 过长或过短, 建议值范围: 4 ~ {max_limit}.')
            return
        self.binance.logger_info(f"=== 验证limit({limit})参数正确.")

        def rv(x):
            """ 公式描述：每日收益的平方加和再开方 """
            return numpy.sqrt(numpy.sum(x ** 2))

        symbol_rv = []
        for item in symbol:
            res = self.binance.rest('while_info', 'mark_price_klines', symbol=item, interval=interval, limit=limit)
            if len(res) < limit:
                continue
            pd_data = set_pandas(res).astype('float')
            pd_data['log return'] = numpy.log(pd_data[4] / pd_data[4].shift(1))  # 对数收益
            pd_data = pd_data.loc[:, ['log return']]
            data_rv = pd_data.apply(rv)
            symbol_rv.append({'symbol': item, 'rv': data_rv.values[0]})
            time.sleep(2)
        symbol_rv.sort(key=lambda s: s['rv'], reverse=False)     # 排序
        self.binance.logger_info(f"获取所有交易对日KBar并计算实际波动率完成.")
        return self.get_depth(symbol_rv)

    def get_depth(self, symbol: list, max_depth_limit=1000):
        """
        获取交易对深度信息
        :param symbol: 交易对名称列表
        :param max_depth_limit: 交易所支持的最大深度数量
        :return: 嵌套字典的交易对列表 {'交易对名称': 'BTCUSDT', '实际波动率': 0.0376, '高深度': 1}
        """
        self.binance.logger_info(f"正在获取交易对深度信息......")
        for i in range(len(symbol)):
            res = self.binance.rest('info', 'depth', symbol=symbol[i]['symbol'], limit=max_depth_limit)
            symbol[i]['depth'] = 0
            if res is not None and len(res['bids']) >= (max_depth_limit * 0.8) <= len(res['asks']):
                symbol[i]['depth'] = 1
            time.sleep(2)
        self.binance.logger_info(f"获取交易对深度信息完成.")
        return [symbol[i] for i in range(len(symbol)) if symbol[i]['depth'] == 1]

    def filter_day(self, symbol: list, day=100):
        """
        过滤新币种
        :param symbol: 交易对名称列表
        :param day: 过滤天数
        :return:
        """
        self.binance.logger_info(f"=== 正在对新交易对币种过滤 ### 过滤天数: {day}......")
        symbols = []
        ex_res = self.binance.rest("while_info", "exchange_info")
        for item in ex_res['symbols']:
            if item['symbol'] in symbol:
                st2 = float(item['onboardDate']) / 1000
                res_day = (datetime.fromtimestamp(time.time()) - datetime.fromtimestamp(st2)).days
                if res_day >= day:
                    symbols.append(item['symbol'])
        self.binance.logger_info(f"=== 完成对新交易对币种的过滤.")
        return symbols
